package top.readhub.wopei.client.zk;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.readhub.wopei.client.helper.InstHelper;
import top.readhub.wopei.common.constants.Constants;
import top.readhub.wopei.common.exception.ExceptionCode;
import top.readhub.wopei.common.exception.WoPeiException;

import java.util.Collections;
import java.util.List;

import static org.apache.curator.framework.recipes.cache.TreeCacheEvent.Type.NODE_REMOVED;
import static org.apache.curator.framework.recipes.cache.TreeCacheEvent.Type.NODE_UPDATED;

/**
 * Created by daervin on 2021/6/7
 */
public class ZKWatcher {
    private static final Logger logger = LoggerFactory.getLogger(ZKWatcher.class);

    private volatile static CuratorFramework zkClient;

    public static void initZKClient() throws WoPeiException {
        if (zkClient != null) {
            return;
        }
        synchronized (ZKWatcher.class) {
            if (zkClient != null) {
                return;
            }
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
            CuratorFramework client = CuratorFrameworkFactory.builder()
                    .connectString("127.0.0.1:2181")
                    .retryPolicy(retryPolicy)
                    .sessionTimeoutMs(6000)
                    .connectionTimeoutMs(3000)
                    .namespace("wopei")
                    .build();
            client.start();
            zkClient = client;
            watch();
        }
    }

    private static void watch() throws WoPeiException {
        final TreeCache treeCache = new TreeCache(zkClient, "/");
        try {
            treeCache.start();
        } catch (Exception e) {
            throw new WoPeiException(e, ExceptionCode.C_ERROR);
        }
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
                if (treeCacheEvent.getData() == null || treeCacheEvent.getData().getData() == null) {
                    return;
                }
                if (!NODE_UPDATED.equals(treeCacheEvent.getType()) && !NODE_REMOVED.equals(treeCacheEvent.getType())) {
                    return;
                }
                String data = new String(treeCacheEvent.getData().getData());
                String path = treeCacheEvent.getData().getPath();
                path = path.replaceAll("/" + Constants.SUB_DEFAULT_PATH, "").replaceAll("/", "");
                logger.info("监听事件触发 {} {}", treeCacheEvent.getType(), data);
                System.out.println("监听事件触发: 节点内容为：" + data);
                if (NODE_UPDATED.equals(treeCacheEvent.getType())) {
                    InstHelper.getLocalLoader().reset(path, data);
                }
                if (NODE_REMOVED.equals(treeCacheEvent.getType())) {
                    InstHelper.getLocalLoader().del(path);
                }
            }
        });
    }

    public static String getData(String path) throws WoPeiException {
        try {
            byte[] bytes = zkClient.getData().forPath(path);
            return new String(bytes, "utf-8");
        } catch (Exception e) {
            throw new WoPeiException(e, ExceptionCode.C_ERROR);
        }
    }

    public static List<String> getChildren(String path) throws WoPeiException {
        try {
            Stat stat = zkClient.checkExists().forPath(path);
            if (stat != null) {
                return zkClient.getChildren().forPath(path);
            }
        } catch (Exception e) {
            throw new WoPeiException(e, ExceptionCode.C_ERROR);
        }
        return Collections.emptyList();
    }

}
